﻿using System;
using System.Linq;
using System.Text;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.Refactoring;
using Xunit;

namespace Framework.Tests.Refactoring
{
    public abstract class ContextActionTestBase
    {
        protected readonly CSharpFormattingOptions FormattingOptions;

        public ContextActionTestBase()
        {
            FormattingOptions = FormattingOptionsFactory.CreateMono();
        }

        internal static string HomogenizeEol(string str)
        {
            var sb = new StringBuilder();
            for (var i = 0; i < str.Length; i++)
            {
                var ch = str[i];
                if (ch == '\n')
                {
                    sb.AppendLine();
                }
                else if (ch == '\r')
                {
                    sb.AppendLine();
                    if (i + 1 < str.Length && str[i + 1] == '\n')
                        i++;
                }
                else
                {
                    sb.Append(ch);
                }
            }
            return sb.ToString();
        }

        public void Test<T>(string input, string output, int action = 0, bool expectErrors = false)
            where T : ICodeActionProvider, new()
        {
            Test(new T(), input, output, action, expectErrors);
        }

        public void Test(ICodeActionProvider provider, string input, string output, int action = 0, bool expectErrors = false)
        {
            var result = RunContextAction(provider, HomogenizeEol(input), action, expectErrors);
            var passed = result == output;
            if (!passed)
            {
                Console.WriteLine("-----------Expected:");
                Console.WriteLine(output);
                Console.WriteLine("-----------Got:");
                Console.WriteLine(result);
            }
            Assert.Equal(HomogenizeEol(output), result);
        }

        protected string RunContextAction(ICodeActionProvider action, string input,
                                                  int actionIndex = 0, bool expectErrors = false)
        {
            var context = TestRefactoringContext.Create(input, expectErrors);
            context.FormattingOptions = FormattingOptions;
            var isValid = action.GetActions(context).Any();

            if (!isValid)
                Console.WriteLine("invalid node is:" + context.GetNode());
            Assert.True(isValid, action.GetType() + " is invalid.");
            using (var script = context.StartScript())
            {
                action.GetActions(context).Skip(actionIndex).First().Run(script);
            }

            return context.doc.Text;
        }

        protected void TestWrongContext<T>(string input) where T : ICodeActionProvider, new()
        {
            TestWrongContext(new T(), input);
        }

        protected void TestWrongContext(ICodeActionProvider action, string input)
        {
            var context = TestRefactoringContext.Create(input);
            context.FormattingOptions = FormattingOptions;
            var isValid = action.GetActions(context).Any();
            if (!isValid)
                Console.WriteLine("invalid node is:" + context.GetNode());
            Assert.True(!isValid, action.GetType() + " shouldn't be valid there.");
        }

        protected void TestActionDescriptions(ICodeActionProvider provider, string input, params string[] expected)
        {
            var ctx = TestRefactoringContext.Create(input);
            ctx.FormattingOptions = FormattingOptions;
            var actions = provider.GetActions(ctx).ToList();
            Assert.Equal(
                expected,
                actions.Select(a => a.Description).ToArray());
        }
    }
}
